/*-----------------------------------------------------------------------------
	The contents of this file are subject to the Mozilla Public License
	Version 1.1 (the "License"); you may not use this file except in
	compliance with the License. You may obtain a copy of the License at
	http://www.mozilla.org/MPL/
	
	Software distributed under the License is distributed on an "AS IS"
	basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
	License for the specific language governing rights and limitations
	under the License.
	
	The Original Code is Fever Framework code.
	
	The Initial Developer of the Original Code is Romain Ecarnot.
	Portions created by Initial Developer are Copyright (C) 2006
	the Initial Developer. All Rights Reserved.
	
	Contributor(s):
	
	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
-----------------------------------------------------------------------------*/

import com.bourre.core.HashCodeFactory;
import com.bourre.events.EventBroadcaster;
import com.bourre.events.IEvent;

import fever.events.PriorityListenerArray;

/**
 * Dispatches events to all added listeners with the help of an {@link BasicEvent} 
 * and using listener priority to call handler in correct order.
 * 
 * <p>The higher the number, the higher the priority. <br />
 * All listeners with priority n are processed before listeners of priority n-1. <br />
 * The default priority is 0.
 * 
 * <p>Can use single access point using {@link #getInstance} and get always the 
 * same #@link PriorityEventBroadcaster} instance.
 * 
 * @see fever.events.EventPriority
 * 
 * @author Romain Ecarnot
 */
class fever.events.PriorityEventBroadcaster extends EventBroadcaster
{
	//-------------------------------------------------------------------------
	// Private properties
	//-------------------------------------------------------------------------
	
	private static var _oI : PriorityEventBroadcaster;
	private var _aAll : PriorityListenerArray;
	
	
	//-------------------------------------------------------------------------
	// Public API
	//-------------------------------------------------------------------------
	
	/**
	 * Returns {@code PriorityEventBroadcaster} instance.
	 * 
	 * <p>Always return the same instance.
	 */
	public static function getInstance() : PriorityEventBroadcaster
	{
		return ( PriorityEventBroadcaster._oI instanceof PriorityEventBroadcaster) 
			? PriorityEventBroadcaster._oI 
			: PriorityEventBroadcaster._buildInstance();
	}
	
	/**
	 * Returns an {@link ListenerArray} of listeners that suscribed for passed-in {@code t} event 
	 * or for all events if parameter is omitted.
	 * 
	 * @param t Event type. If event type is undefined, method will return listeners that suscribed for all events.
	 * 
	 * @return a {@link fever.events.PriorityListenerArray} instance
	 */
	public function getListenerArray( t : String ) : PriorityListenerArray
	{
		return ( t == undefined ) ? _aAll : _oL[t];
	}
	
	/**
	 * Adds passed-in {@code listener} for receiving all events.
	 * 
	 * @param listener Listener object
	 * @param f (optional) new callback function
	 * @param priority ( optionnal ) listener priority
	 */
	public function addListener( listener, f : Function, priority : Number ) : Void
	{
		var p : Number = arguments[ arguments.length - 1];
		
		if (f) listener = _getEventProxy.apply( this, arguments );
		if ( _aAll.insert( listener, p ) ) 
			_clean( f ? HashCodeFactory.getKey( listener.t ) : HashCodeFactory.getKey( listener ) );
	}
	
	/**
	 * Adds passed-in {@code listener} for receiving all events.
	 * 
	 * @param listener Listener object
	 * @param priority ( optionnal ) listener priority
	 */
	public function addPriorityListener( listener, priority : Number ) : Void
	{
		addListener( listener, null, priority );	
	}
	
	/**
	 * Adds passed-in {@code listener} for receiving passed-in event {@code type} event.
	 * 
	 * <p>As we can pass many arguments in method call, priority value is retreive using 
	 * the {@code argument.length - 1} argument index.
	 * {@code
	 *   // priority == 10
	 *   ob.addEventListener( "MyEvent", this, _handleCallback, "param1", "param2", 10 );
	 * }
	 * 
	 * @param t Name of the Event.
	 * @param oL Listener object.
	 */
	public function addEventListener( type : String, listener ) : Void 
	{
		var f : Function = arguments[2];
		var priority : Number = arguments[ arguments.length - 1];
		
		if (f) listener = _getEventProxy.apply( this, arguments.splice(1) );
		if ( !_aAll.listenerExists( listener ) )
		{
			if ( !listenerArrayExists( type ) ) _oL[ type ] = new PriorityListenerArray();
			if ( getListenerArray( type ).insert( listener, priority ) )
			{
				var n:Number = f ? HashCodeFactory.getKey( listener.t ) : HashCodeFactory.getKey( listener );
				if (_oE[n] == undefined) _oE[n] = new Object();
				_oE[n][ type ] = listener;
			}
		}
	}
	
	/**
	 * Broadcasts event to suscribed listeners.
	 * 
	 * <p>A listener sorting is applied before broadcasting.
	 * 
	 * @param e an {@link IEvent} instance
	 */
	public function broadcastEvent( e : IEvent ) : Void
	{
		if ( e.getTarget() == undefined ) e.setTarget( _oOwner );
		var aL : PriorityListenerArray = getListenerArray( e.getType() );
		
		if (aL != undefined) _broadcast(aL, e);
		if (_aAll.length > 0) _broadcast(_aAll, e);
	}
	
	
	//-------------------------------------------------------------------------
	// Private implementation
	//-------------------------------------------------------------------------
	
	private static function _buildInstance() : PriorityEventBroadcaster
	{
		PriorityEventBroadcaster._oI = new PriorityEventBroadcaster();
		return _oI;
	}
	
	private function _init() : Void
	{
		_oL = new Object();
		_aAll = new PriorityListenerArray();
		_oE = new Object();
	}
	
	private function _broadcast( aL : PriorityListenerArray, e : IEvent) : Void
	{
		var l : Number = aL.length;
		aL.sortOn( "priority", Array.NUMERIC );
		
		while( --l > -1 )
		{
			var o = aL[l].listener;
			var sType : String = typeof( o );
			
			if (sType == "object" || sType == "movieclip") 
			{
				if ( o.handleEvent != undefined ) o.handleEvent(e);
				else o[ String( e.getType() ) ](e);
			} 
			else o.apply( this, [e] );
		}
	}	
}